package com.hmy.security.controller;

import com.alibaba.fastjson2.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.hmy.security.constant.CacheConstant;
import com.hmy.security.dto.LoginDto;
import com.hmy.security.entity.User;
import com.hmy.security.security.annotation.OpenAccess;
import com.hmy.security.security.bean.JwtUser;
import com.hmy.security.service.IUserService;
import com.hmy.security.util.CacheUtil;
import com.hmy.security.util.TokenUtil;
import com.hmy.security.vo.LoginResultVo;
import com.hmy.security.vo.VerifyCodeVo;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/auth")
public class AuthController implements InitializingBean {

    @Value("${security.single-login}")
    private Boolean singleLogin;

    Cache<String, String> verifyCodeCache;

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManagerBuilder authenticationManagerBuilder;
    @Autowired
    private IUserService userService;
    @Autowired
    private CacheUtil cacheUtil;

    @OpenAccess
    @PostMapping("/login")
    LoginResultVo login(@Validated LoginDto loginDto) {
        String cacheCode = verifyCodeCache.getIfPresent(loginDto.getUuid());
        LoginResultVo loginResultVo = new LoginResultVo();
        if (StringUtils.isEmpty(cacheCode)) {
            loginResultVo.setMessage("本次会话已失效，请刷新后重试");
            return loginResultVo;
        }
        if (!loginDto.getVerifyCode().equalsIgnoreCase(cacheCode)) {
            loginResultVo.setMessage("验证码错误");
            return loginResultVo;
        }

        User user = userService.getUserByName(loginDto.getUsername());

        if (!this.passwordEncoder.matches(loginDto.getPassword(), user.getPassword())) {
            loginResultVo.setMessage("密码错误");
            return loginResultVo;
        }

        // 此处传递的password为未加密的密码
        // 后续会{@link DaoAuthenticationProvider#additionalAuthenticationChecks}方法内进行一次密码校验}
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUsername(), loginDto.getPassword());
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        final JwtUser userDetails = (JwtUser) authentication.getPrincipal();
        String token = TokenUtil.genToken(loginDto.getUsername());
        // 将token以及userDetails存储至缓存中
        String userTokenKey = String.format(CacheConstant.USER_TOKEN_KEY, user.getUid());
        if (singleLogin) {
            // 如果开启了单用户登录 删除掉缓存的其他token
            cacheUtil.del(userTokenKey);
        }
        cacheUtil.lSet(userTokenKey, token);
        cacheUtil.set(token, JSON.toJSONString(userDetails));

        loginResultVo.setSuccess(true);
        loginResultVo.setMessage("登陆成功");
        loginResultVo.setToken(token);
        return loginResultVo;
    }

    @OpenAccess
    @GetMapping("/verify/img")
    VerifyCodeVo genVerifyCode() {
        String uuid = UUID.randomUUID().toString();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 4; i++) {
            sb.append((char) RandomUtils.nextInt(65, 90));
        }
        verifyCodeCache.put(uuid, sb.toString());
        VerifyCodeVo verifyCodeVo = new VerifyCodeVo();
        verifyCodeVo.setUuid(uuid);
        verifyCodeVo.setImage(sb.toString());
        return verifyCodeVo;

    }

    @DeleteMapping(value = "/logout")
    Boolean logout(HttpServletRequest request) {
        String token = TokenUtil.getToken(request);
        if (StringUtils.isNotBlank(token)) {
            // 直接删除token即可
            JwtUser userDetail = JSON.parseObject(cacheUtil.get(token), JwtUser.class);
            cacheUtil.del(token);
            cacheUtil.lRm(String.format(CacheConstant.USER_TOKEN_KEY, userDetail.getUid()), token);
        }
        return true;
    }



    @Override
    public void afterPropertiesSet() throws Exception {
        verifyCodeCache = CacheBuilder.newBuilder()
                .expireAfterWrite(3, TimeUnit.MINUTES)
                // .expireAfterAccess(1, TimeUnit.MILLISECONDS)
                .build();
    }
}
